﻿using System.Collections.Generic;
using Ymatou.User.Dto.Auth;
using YmtAuth.Domain.Model.Safe;
using YmtAuth.Domain.Repository;
using YmtAuth.Dto;
using YmtSystem.CrossCutting;
using System.Linq;
using System;
using ServiceStack;
using YmtAuth.Domain;
using YmtAuth.Common;

namespace YmtAuth.AppService
{
    public class SecurityQuestionAppService : ISecurityQuestionAppService
    {
        /// <summary>
        /// 安全问题仓储实例
        /// </summary>
        private ISecurityQuestionRepository sqRepo { get { return LocalServiceLocator.GetService<ISecurityQuestionRepository>(); } }

        /// <summary>
        /// 安全问题：每个用户需要设置的安全问题的个数
        /// </summary>
        private static int SecurityQuestionCountPerUser
        {
            get
            {
                var _SecurityQuestionCountPerUser = System.Configuration.ConfigurationManager.AppSettings["SecurityQuestionCountPerUser"];
                return _SecurityQuestionCountPerUser.ConvertToInt32(3);
            }
        }

        /// <summary>
        /// 安全问题：每个用户每天最多能校验安全问题的次数
        /// </summary>
        private static int MaxSecurityQuestionVerifyCountPerDay
        {
            get
            {
                var _MaxSecurityQuestionVerifyCountPerDay = System.Configuration.ConfigurationManager.AppSettings["MaxSecurityQuestionVerifyCountPerDay"];
                return _MaxSecurityQuestionVerifyCountPerDay.ConvertToInt32(2);
            }
        }

        /// <summary>
        /// 获取安全问题列表
        /// 200, 获取安全问题列表成功
        /// 500, 出现异常
        /// </summary>
        /// <returns></returns>
        public ResponseData<List<SecurityQuestion>> GetSecurityQuestionList()
        {
            return sqRepo.GetSecurityQuestionList();
        }

        /// <summary>
        /// 设置安全问题
        /// 流程：
        /// 1. 检查参数
        ///     -100, 用户ID非法
        ///     -200, 安全问题个数不对
        ///     -300, 非法的问题ID
        /// 2. 设置安全问题
        ///     a) 100, 设置安全问题失败（更新数据库失败）
        ///     b) 200, 设置安全问题成功
        ///     c) 500, 出现异常
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public ResponseData<bool> SetSecurityQuestion(SetSecurityQuestionRequestDto dto)
        {
            //检查参数
            YmtSystemAssert.AssertArgumentNotNull(dto, "无效的SetSecurityQuestionRequestDto");
            YmatouLoggingService.Debug("【设置安全问题】[SetSecurityQuestion], SetSecurityQuestionRequestDto:{0}", dto.ToJson());

            if (dto.UserId <= 0)
                return ResponseData<bool>.Create(false, true, "用户ID非法: " + dto.UserId, SetSecurityQuestionResponseCode.InvalidUserId);

            int actualSecurityQuestionCount = dto.SecurityQuestionAnswerList == null ? 0 : dto.SecurityQuestionAnswerList.Count();

            if (actualSecurityQuestionCount != SecurityQuestionCountPerUser)
                return ResponseData<bool>.Create(false, true, string.Format("安全问题个数不对。期望：{0}, 实际：{1} ", SecurityQuestionCountPerUser, actualSecurityQuestionCount), SetSecurityQuestionResponseCode.InvalidSecurityQuestionCount);

            ResponseData<List<SecurityQuestion>> getSecurityQuestionRes = sqRepo.GetSecurityQuestionList();

            if (getSecurityQuestionRes.Success)
            {
                if (getSecurityQuestionRes.Result != null)
                {

                    foreach (SecurityQuestionAnswer answer in dto.SecurityQuestionAnswerList)
                    {
                        if (!getSecurityQuestionRes.Result.Any(r => r.QuestionId == answer.QuestionId))
                            return ResponseData<bool>.Create(false, true, string.Format("非法的问题ID: {0}", answer.QuestionId), SetSecurityQuestionResponseCode.InvalidQuestionId);
                    }
                }
            }
            else
            {
                return ResponseData<bool>.Create(false, true, getSecurityQuestionRes.LastErrorMessage, SetSecurityQuestionResponseCode.InvalidQuestionId);
            }

            ResponseData<bool> response = sqRepo.SetSecurityQuestion(dto.UserId, dto.SecurityQuestionAnswerList);

            sqRepo.ResetUserVerifyCount(dto.UserId, ConfigUtility.NextSecurityQuestionVerifyDayCount);

            YmatouLoggingService.Debug("[SetSecurityQuestion] result, userId: {0}, response: {1}", dto.UserId, response.ToJson());

            return response;
        }

        /// <summary>
        /// 获取已设置的安全问题（单个，For验证）
        /// 流程：
        /// 1. 检查参数(UserId > 0)
        ///     -100, 用户ID非法
        ///    400, 此用户未设置过安全问题
        ///    500, 获取用户当前的验证状态出现异常
        /// 2. 获取用户当天的验证次数，如果不小于配置项 "MaxSecurityQuestionVerifyCountPerDay" 的值，返回("验证次数已用完")
        ///     0, 验证次数已用完
        /// 3. 获取安全问题
        ///     a) 200, 获取到安全问题
        ///     b  400, 此用户未设置过安全问题
        ///     c) 500, 获取用户安全问题出现异常
        ///     d) 600, 无可用的用户安全问题, 当天已验证过（通过或不通过）所有的用户安全问题
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public ResponseData<GetSecurityQuestionResponseDto> GetSecurityQuestion(GetSecurityQuestionRequestDto dto)
        {
            // 1. 检查参数(UserId > 0)
            YmtSystemAssert.AssertArgumentNotNull(dto, "无效的GetSecurityQuestionRequestDto");
            YmatouLoggingService.Debug("【获取已设置的安全问题】GetSecurityQuestion, GetSecurityQuestionRequestDto: {0}", dto.ToJson());

            if (dto.UserId <= 0)
                return ResponseData<GetSecurityQuestionResponseDto>.Create(new GetSecurityQuestionResponseDto()
                {
                    Code = SecurityQuestionResponseCode.InvalidUserId,
                    Msg = "用户ID非法: " + dto.UserId
                }, true);

            ResponseData<GetSecurityQuestionResponseDto> response = null;

            List<UserSecurityQuestion> userSecurityQuestionList = sqRepo.GetUserSecurityQuestion(dto.UserId);

            //确认用户设置过安全问题
            if (userSecurityQuestionList.Any())
            {
                // 2. 获取用户当前的验证次数，如果不小于配置项 "MaxSecurityQuestionVerifyCountPerDay" 的值，返回Code 10("验证次数已用完"); AvailbleVerifyCount = 0、NextVerifyDayCount = 2
                ResponseData<UserSecurityQuestionVerifyStatus> userVerifyStatusRes = sqRepo.GetCurrentVerifyStatus(dto.UserId, ConfigUtility.NextSecurityQuestionVerifyDayCount);

                if (userVerifyStatusRes.Success)
                {
                    UserSecurityQuestionVerifyStatus userVerifyStatus = userVerifyStatusRes.Result;

                    int availableVerifyCount = MaxSecurityQuestionVerifyCountPerDay - userVerifyStatus.VerifyCount;
                    int nextVerifyDayCount = ConfigUtility.NextSecurityQuestionVerifyDayCount;
                    if (userVerifyStatus.LatestVerifyTime.HasValue)
                    {
                        nextVerifyDayCount = ConfigUtility.NextSecurityQuestionVerifyDayCount - (DateTime.Now.Date.Subtract(userVerifyStatus.LatestVerifyTime.Value.Date).Days);
                    }

                    // 验证次数已用完
                    if (availableVerifyCount <= 0)
                    {
                        response = ResponseData<GetSecurityQuestionResponseDto>.Create(new GetSecurityQuestionResponseDto()
                        {
                            Code = SecurityQuestionResponseCode.VerifyCountUsedUp,
                            Msg = "验证次数已用完",
                            NextVerifyDayCount = nextVerifyDayCount
                        }, true);
                    }
                    else
                    {
                        ResponseData<UserSecurityQuestion> res = sqRepo.GetSecurityQuestion(dto.UserId, userSecurityQuestionList, ConfigUtility.NextSecurityQuestionVerifyDayCount);

                        if (res.Success)
                        {
                            if (res.ErrorCode == SecurityQuestionResponseCode.Success)
                            {
                                response = ResponseData<GetSecurityQuestionResponseDto>.Create(new GetSecurityQuestionResponseDto()
                                {
                                    Code = 200,
                                    Msg = "success",
                                    NextVerifyDayCount = 0,
                                    UserQuestionId = res.Result.UserQuestionId,
                                    QuestionContent = res.Result.QuestionContent,
                                    QuestionTip = res.Result.QuestionTip
                                }, true);
                            }
                            else
                            {
                                response = ResponseData<GetSecurityQuestionResponseDto>.Create(new GetSecurityQuestionResponseDto()
                                {
                                    Code = res.ErrorCode.ToInt(100),
                                    Msg = res.LastErrorMessage,
                                    NextVerifyDayCount = nextVerifyDayCount
                                }, true);
                            }
                        }
                        else
                        {
                            response = ResponseData<GetSecurityQuestionResponseDto>.CreateFail(new GetSecurityQuestionResponseDto()
                            {
                                NextVerifyDayCount = nextVerifyDayCount
                            }, res.ErrorCode, res.LastErrorMessage);
                        }
                    }
                }
                else
                {
                    response = ResponseData<GetSecurityQuestionResponseDto>.CreateFail(new GetSecurityQuestionResponseDto()
                    {
                        NextVerifyDayCount = ConfigUtility.NextSecurityQuestionVerifyDayCount
                    }, userVerifyStatusRes.ErrorCode, userVerifyStatusRes.LastErrorMessage);
                }
            }
            else
            {
                response = ResponseData<GetSecurityQuestionResponseDto>.Create(new GetSecurityQuestionResponseDto()
                {
                    Code = SecurityQuestionResponseCode.UserHasNotSetSecurityQuestion.ToInt(100),
                    Msg = "此用户未设置过安全问题",
                    NextVerifyDayCount = ConfigUtility.NextSecurityQuestionVerifyDayCount
                }, true);
            }

            return response;
        }

        /// <summary>
        /// 验证安全问题
        /// 流程：
        /// 1. 检查参数(UserId > 0, UserQuestionId > 0, QuestionAnswer不为空)
        ///     a) -100, 用户ID非法
        ///     b) -200, 用户安全问题ID非法
        ///     c) -300, 用户问题答案不能为空
        /// 2. 获取用户当天的验证次数，如果不小于配置项 "MaxSecurityQuestionVerifyCountPerDay" 的值，返回("验证次数已用完")
        ///     0, 验证次数已用完
        /// 3. 验证安全问题答案
        ///     a) 100, 验证用户安全问题失败（答案错误,当天的验证次数加1）
        ///     b) 200, 验证通过（当天的验证次数清零）
        ///     c) 300, 用户没有设置过此安全问题
        ///     d) 400, 此用户未设置过安全问题
        ///     e) 500, 验证安全问题出现异常
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public ResponseData<VerifySecurityQuestionResponseDto> VerifySecurityQuestion(VerifySecurityQuestionRequestDto dto)
        {
            //检查参数
            YmtSystemAssert.AssertArgumentNotNull(dto, "无效的VerifySecurityQuestionRequestDto");
            int userId = dto.UserId;

            YmatouLoggingService.Debug("【VerifySecurityQuestion】, VerifySecurityQuestionRequestDto, userId: {0}, UserQuestionId: {1}, answer: {2}", userId, dto.UserQuestionId, dto.QuestionAnswer);
            
            if (userId <= 0)
                return ResponseData<VerifySecurityQuestionResponseDto>.Create(new VerifySecurityQuestionResponseDto()
                {
                    Code = SecurityQuestionResponseCode.InvalidUserId,
                    Msg = "用户ID非法: " + userId
                }, true);
            if (dto.UserQuestionId <= 0)
                return ResponseData<VerifySecurityQuestionResponseDto>.Create(new VerifySecurityQuestionResponseDto()
                {
                    Code = VerifySecurityQuestionResponseCode.InvalidUserQuestionId,
                    Msg = "用户安全问题ID非法: " + dto.UserQuestionId
                }, true);
            if (string.IsNullOrEmpty(dto.QuestionAnswer))
                return ResponseData<VerifySecurityQuestionResponseDto>.Create(new VerifySecurityQuestionResponseDto()
                {
                    Code = VerifySecurityQuestionResponseCode.NullQuestionAnswer,
                    Msg = "用户问题答案不能为空"
                }, true);

            ResponseData<VerifySecurityQuestionResponseDto> response = null;

            ResponseData<UserSecurityQuestionVerifyStatus> userVerifyStatusRes = sqRepo.GetCurrentVerifyStatus(userId, ConfigUtility.NextSecurityQuestionVerifyDayCount);

            if (userVerifyStatusRes.Success)
            {
                UserSecurityQuestionVerifyStatus userVerifyStatus = userVerifyStatusRes.Result;

                int availableVerifyCount = MaxSecurityQuestionVerifyCountPerDay - userVerifyStatus.VerifyCount;
                int nextVerifyDayCount = ConfigUtility.NextSecurityQuestionVerifyDayCount;
                if (userVerifyStatus.LatestVerifyTime.HasValue)
                {
                    nextVerifyDayCount = ConfigUtility.NextSecurityQuestionVerifyDayCount - (DateTime.Now.Date.Subtract(userVerifyStatus.LatestVerifyTime.Value.Date).Days);
                }

                if (availableVerifyCount <= 0)
                {
                    response = ResponseData<VerifySecurityQuestionResponseDto>.Create(new VerifySecurityQuestionResponseDto()
                    {
                        Code = SecurityQuestionResponseCode.VerifyCountUsedUp,
                        Msg = "验证次数已用完",
                        NextVerifyDayCount = nextVerifyDayCount,
                        AvailbleVerifyCount = availableVerifyCount
                    }, true);
                }
                else
                {
                    ResponseData<bool> res = sqRepo.VerifySecurityQuestion(userId, dto.UserQuestionId, dto.QuestionAnswer);

                    if (res.Success)
                    {
                        if (res.Result)
                        {
                            response = ResponseData<VerifySecurityQuestionResponseDto>.CreateSuccess(new VerifySecurityQuestionResponseDto()
                            {
                                Code = 200,
                                Msg = "验证通过",
                                NextVerifyDayCount = 0,
                                AvailbleVerifyCount = MaxSecurityQuestionVerifyCountPerDay
                            }, "success");

                            ResetUserSecurityQuestionVerifyCount(userId);
                        }
                        else
                        {
                            if (string.Equals(VerifySecurityQuestionResponseCode.WrongAnswer, res.ErrorCode))
                            {
                                availableVerifyCount = availableVerifyCount - 1;
                                if (availableVerifyCount > 0)
                                {
                                    response = ResponseData<VerifySecurityQuestionResponseDto>.Create(new VerifySecurityQuestionResponseDto()
                                    {
                                        Code = res.ErrorCode.ToInt(100),
                                        Msg = res.LastErrorMessage,
                                        NextVerifyDayCount = 0,
                                        AvailbleVerifyCount = availableVerifyCount
                                    }, true);
                                }
                                else
                                {
                                    response = ResponseData<VerifySecurityQuestionResponseDto>.Create(new VerifySecurityQuestionResponseDto()
                                    {
                                        Code = res.ErrorCode.ToInt(100),
                                        Msg = res.LastErrorMessage,
                                        NextVerifyDayCount = ConfigUtility.NextSecurityQuestionVerifyDayCount,
                                        AvailbleVerifyCount = 0
                                    }, true);
                                }

                                sqRepo.VerifyFailHandle(userId, dto.UserQuestionId, dto.QuestionAnswer, ConfigUtility.NextSecurityQuestionVerifyDayCount);
                            }
                            else
                            {
                                response = ResponseData<VerifySecurityQuestionResponseDto>.CreateFail(new VerifySecurityQuestionResponseDto()
                                {
                                    NextVerifyDayCount = nextVerifyDayCount,
                                    AvailbleVerifyCount = availableVerifyCount
                                }, res.ErrorCode, res.LastErrorMessage);
                            }
                        }
                    }
                    else
                    {
                        response = ResponseData<VerifySecurityQuestionResponseDto>.CreateFail(new VerifySecurityQuestionResponseDto()
                        {
                            NextVerifyDayCount = ConfigUtility.NextSecurityQuestionVerifyDayCount,
                            AvailbleVerifyCount = availableVerifyCount
                        }, res.ErrorCode, res.LastErrorMessage);
                    }
                }
            }
            else
            {
                response = ResponseData<VerifySecurityQuestionResponseDto>.CreateFail(new VerifySecurityQuestionResponseDto()
                {
                    NextVerifyDayCount = ConfigUtility.NextSecurityQuestionVerifyDayCount
                }, userVerifyStatusRes.ErrorCode, userVerifyStatusRes.LastErrorMessage);
            }

            YmatouLoggingService.Debug("【VerifySecurityQuestion】 result, userId: {0}, response: {1}", userId, response.ToJson());

            return response;
        }

        /// <summary>
        /// 安全问题校验的尝试次数清零
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public ResponseData<bool> ResetUserSecurityQuestionVerifyCount(int userId)
        {
            try 
            {
                sqRepo.ResetUserVerifyCount(userId, ConfigUtility.NextSecurityQuestionVerifyDayCount);
            }
            catch (Exception ex)
            {
                string msg = string.Format("ResetUserSecurityQuestionVerifyCount error, userId: {0}, msg: {1}", userId, ex.Message);
                YmatouLoggingService.Error(msg, ex);

                return ResponseData<bool>.Create(false, false, msg, "400");
            }

            return ResponseData<bool>.Create(true, true, "ok", "200");
        }
    }
}
